﻿// Copyright (c) 2012 Mokah Nnamezie & Andrew Cooper
// Neo4Net graph database - http://neo4net.codeplex.com
//
// This file is part of Neo4Net - a .Net port of the Neo4j graph database
//
// Neo4Net is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace Neo4Net.Helpers.Collection
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using NUnit.Framework;

    [TestFixture]
    public class ExceptionHandlingEnumerableTest
    {
        [Test]
        [ExpectedException(ExpectedException = typeof(InvalidOperationException))]
        public void TestHandleExceptionOnEnumeratorCreation()
        {
            new ExceptionHandlingEnumerable<int>(new ThrowingEnumerable<int>(true)).Count();
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(InvalidOperationException))]
        public void TestHandleExceptionOnMoveNext()
        {
            new ExceptionHandlingEnumerable<int>(new ThrowingEnumerable<int>(false, true)).Count();
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(InvalidOperationException))]
        public void TestHandleExceptionOnCurrent()
        {
            new ExceptionHandlingEnumerable<int>(new ThrowingEnumerable<int>(false, false, true)).Count();
        }

        private class ThrowingEnumerable<T> : IEnumerable<T>, IEnumerator<T>
        {
            private readonly bool throwOnGetEnumerator;
            private readonly bool throwOnMoveNext;
            private readonly bool throwOnCurrent;

            public ThrowingEnumerable(bool throwOnGetEnumerator = false, bool throwOnMoveNext = false, bool throwOnCurrent = false)
            {
                this.throwOnGetEnumerator = throwOnGetEnumerator;
                this.throwOnMoveNext = throwOnMoveNext;
                this.throwOnCurrent = throwOnCurrent;
            }

            public IEnumerator<T> GetEnumerator()
            {
                if (throwOnGetEnumerator) throw new NotImplementedException("Exception on GetEnumerator");
                return this;
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            public T Current
            {
                get 
                {
                    if (throwOnCurrent) throw new NotImplementedException("Exception on Current");
                    return default(T);
                }
            }

            public void Dispose()
            {
            }

            object IEnumerator.Current
            {
                get 
                { 
                    if (throwOnCurrent) throw new NotImplementedException("Exception on Current");
                    return null;
                }
            }

            public bool MoveNext()
            {
                if (throwOnMoveNext) throw new NotImplementedException("Exception on MoveNext");
                return true;
            }

            public void Reset()
            {
                throw new NotImplementedException("Exception on Reset");
            }
        }

        private class InvalidOperationExceptionHandlingEnumerable<T> : ExceptionHandlingEnumerable<T>
        {
            public InvalidOperationExceptionHandlingEnumerable(IEnumerable<T> enumerable)
                : base(enumerable)
            {
            }

            protected override bool ExceptionOnMoveNext(Exception ex)
            {
                Rethrow(new InvalidOperationException());
                return base.ExceptionOnMoveNext(ex);
            }

            protected override bool ExceptionOnCurrent(Exception ex)
            {
                Rethrow(new InvalidOperationException());
                return base.ExceptionOnCurrent(ex);
            }

        }
    }
}
